/* This file is part of BonQ - A simple HTTP-based messaging server.
 * Copyright (C) 2009 James Cook
 *
 * BonQ is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License, version 2, as published by the
 * Free Software Foundation.
 *
 * BonQ is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License, version 2,
 * for more details.
 *
 * You should have received a copy of the GNU General Public License, version
 * 2, along with BonQ.  If not, see:
 *
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 */
package com.bonkabonka.queue;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

/**
 * Queue that runs a script against every message added.  For example:
 * <pre>
 * function process(name)
 * {
 *     var queue = queues.get(name);
 *     return 'I peeked and found this: \'' + queue.peek() + '\'';
 * }
 * </pre>
 * A script is only required to provide the "process" function.  Provided to
 * the environment is the "queues" object, which is the
 * <code>QueueBucket</code>.
 * 
 * @see QueueBucket
 * 
 * @author jamesc
 */
public class ScriptedQueue extends StatusQueue
{
    private String script;
    private transient ScriptEngine engine;
    
    /**
     * Initialize the queue.
     * 
     * @param bucket QueueBucket containing the Queue
     */
    @Override
    public void init(QueueBucket bucket, String name)
    {
        synchronized(this)
        {
            super.init(bucket, name);

            // If the script isn't null, we were deserialized and need to make
            // sure the engine is set up.
            if(script != null)
            {
                configure(script);
            }
        }
    }
    
    /**
     * Accept a replacement script.
     * 
     * @param script String containing the script to use to handle messages
     * @return String containing the result of executing the script
     */
    @Override
    public String configure(String script)
    {
        String result = null;

        synchronized(this)
        {
            this.script = script;
        
            if(script != null)
            {
                try
                {
                    ScriptEngineManager manager = new ScriptEngineManager();
                    engine = manager.getEngineByName("JavaScript");
                    if(engine == null || !(engine instanceof Invocable))
                    {
                        throw new ScriptException("Invalid JavaScript engine");
                    }

                    engine.put("queues", getBucket());

                    Object o = engine.eval(script);
                    if(o != null)
                    {
                        result = o.toString();
                    }
                }
                catch(ScriptException se)
                {
                    se.printStackTrace(System.err);
                    result = "ERR: " + se.getMessage();
                }
            }
            else
            {
                engine = null;
                result = "ERR: null script";
            }
        }
            
        return result;
    }
    
    /**
     * Receive a message to process.
     * 
     * @param message String containing the message to process
     * @return String containing the result of the script processing
     */
    @Override
    public String push(String message)
    {
        String result = null;
        
        synchronized(this)
        {
            if(engine != null && script != null)
            {
                try
                {
                    Object o = ((Invocable)engine).invokeFunction("process",
                                                                  message);
                    if(o != null)
                    {
                        result = o.toString();
                    }
                }
                catch(Exception e)
                {
                    result = "ERR: " + e.getMessage();
                }
            }
            else
            {
                result = "ERR: null script";
            }
        }
        
        return result;
    }
    
    /**
     * Peek at the script.
     * 
     * @return String containing the script attached to the queue
     */
    @Override
    public String peek()
    {
        return script;
    }
}
